<?php
namespace App\Services\OpenWechat;
use App\Exceptions\ApiErrCode;
use App\Http\Controllers\Admin\Store\StoreStaffController;
use App\Http\Controllers\Wechat\WechatUserController;
use App\Models\Admin\StoreAdmin;
use App\Models\Coupon\CouponCard;
use App\Models\Coupon\CouponTicket;
use App\Models\OpenWechat\ScanEvent;
use App\Models\OpenWechat\WechatUser;
use App\Services\OpenWechat\Contracts\EventMessageHandler;
use App\Services\Locale\Language;
use App\Services\Utils\TimeHelper;
use EasyWeChat\Kernel\Messages\Image;
use EasyWeChat\Kernel\Messages\News;
use EasyWeChat\Kernel\Messages\NewsItem;
use EasyWeChat\Kernel\Messages\Video;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Str;

/**
 * 开放平台扫码事件处理器
 *
 * Class ScanEventHandler
 * @package App\Services\OpenWechat\Goat
 *
 * @mixin Builder
 */
class ScanEventHandler implements EventMessageHandler
{
    const NEWS_IMAGE_BOOM_GAME = 'https://gz.bcebos.com/v1/beauty-mirror/boom_game/wx_news_cover_game.png';

    const NEWS_IMAGE_COUPON_CARD = 'https://gz.bcebos.com/v1/beauty-mirror/boom_game/wx_news_cover_coupon_card.png';
    /**
     * @var ScanEvent
     */
    private $scan_event;

    /**
     * 用 event key 注册
     * @param $event_key
     * @return null|self
     */
    public static function registerEventKey($event_key): ?self
    {
        if (! Str::startsWith($event_key, ScanEvent::PREFIX)) {
            return null;
        }

        $scan_code_id = last(explode(ScanEvent::SEPARATOR, $event_key));
        $scan_event = (new ScanEvent)->find($scan_code_id);
        if (! $scan_event) {
            return null;
        }

        return new self($scan_event);
    }

    /**
     * ScanEventHandler constructor.
     * @param ScanEvent $scan_event
     */
    public function __construct(ScanEvent $scan_event)
    {
        $this->scan_event = $scan_event;

        // set app locale from scan code
        Language::setAppLocale($scan_event->data['language'] ?? Language::ZH_CN);
    }

    /**
     * @param array $message
     * @return mixed
     */
    public function response(array $message)
    {
        return $this->responseByScanEventType($message);
    }

    /**
     * @param $message
     * @return null|string
     */
    private function responseByScanEventType(array $message)
    {
        switch ($this->scan_event->type) {
            case ScanEvent::TYPE_PAD_USER_LOGIN :
                return $this->handlePadUserLogin($message);

            case ScanEvent::TYPE_STORE_STAFF_BIND :
                return $this->handleStoreAdminBind($message);

            case ScanEvent::TYPE_APP_STORE_STAFF_BIND :
                return $this->handleAppBindingStaff($message);

            case ScanEvent::TYPE_REPLY_IMAGE :
                return $this->handleReplyImage();

            case ScanEvent::TYPE_REPLY_VIDEO :
                return $this->handleReplyVideo();

            case ScanEvent::TYPE_BOOM_GAME :
                return $this->handleBoomGame($message);

            case ScanEvent::TYPE_COUPON_CARD :
                return $this->handleCouponCard($message);

            default :
                $this->logInvalidateScanEvent($message);
                return null;
        }
    }

    /**
     * 处理Pad扫码登录
     * @test
     *
     * @uses \App\Http\Controllers\App\WechatUserController::loginQrCode()
     *
     * @param $message
     * @see Mock::serverEventScanMessage()
     * @return string
     */
    private function handlePadUserLogin(array $message)
    {
        $open_id = $message['FromUserName'];

        /** @var WechatUser $wechat_user */
        $wechat_user = WechatUser::findByOpenId($open_id);

        if (! $wechat_user) {
            return trans('service.wechat_user', ['open_id'=>$open_id]);
        } else {
            $this->syncWechatUserLanguage($wechat_user);

            if ($wechat_user->hasPersonalInfo()) {
                $this->scan_event->update([
                    'value'      => $wechat_user->id,
                    'scan_count' => $this->scan_event->scan_count + 1,
                ]);
                $this->scan_event->incrementWechatUserVisitStore();

                return trans('service.nick_name', [
                    'nick_name'       => $wechat_user->nickname,
                    'user_center_url' => $wechat_user->wxUserCenterUrl(),
                ]);
            } else {
                $info_url = $wechat_user->appWechatScanLoginFillUserInfoUrl($this->scan_event->id);

                /**
                 * @uses WechatUserController::update() - 跳转这里更新数据
                 */
                return trans('service.decode_nick_name', [
                    'nick_name' => $wechat_user->decodeNickName(),
                    'info_url'  => $info_url
                ]);
            }
        }
    }

    /**
     * 处理app绑定店员事件
     * @param array $message
     * @return array|\Illuminate\Contracts\Translation\Translator|null|string
     */
    private function handleAppBindingStaff(array $message)
    {
        if ($this->scan_event->isScanned()) {
            return trans('service.qrcode');
        }

        $this->scan_event->increment('scan_count');

        $open_id = $message['FromUserName'];

        /** @var WechatUser $wechat_user */
        $wechat_user = WechatUser::findByOpenId($open_id);

        if (! $wechat_user) return trans('service.wechat_user', ['open_id'=>$open_id]);

        $this->scan_event->update([
            'value' => $wechat_user->id,
        ]);

        if ($wechat_user->storeAdmin) {
            return '扫码成功，你的微信已经绑定店员，请先在店铺后台取消绑定，再重新扫码';
        }

        // trans 微信消息从 wechat user language 上判断 app local 吗？
        return '扫码成功，等待提交店员相关信息保存';
    }


    /**
     * @param array $message
     */
    private function logInvalidateScanEvent(array $message)
    {
        logger(ApiErrCode::LOG_PREFIX . self::class . ' Invalidate ScanEvent');

        logger($this->scan_event->toArray());

        logger($message);
    }

    /**
     * 处理店铺店员微信绑定扫码事件
     * @see StoreStaffController::bindQrCode()
     *
     * @param array $message
     * @return string
     */
    private function handleStoreAdminBind(array $message): string
    {
        if ($this->scan_event->isScanned()) {
            return trans('service.qrcode');
        }

        $this->scan_event->increment('scan_count');

        $open_id = $message['FromUserName'];

        /** @var WechatUser $wechat_user */
        $wechat_user = WechatUser::findByOpenId($open_id);
        if ($wechat_user) {
            $store_admin_id = $this->scan_event->value;

            /** @var StoreAdmin $admin */
            $admin = StoreAdmin::find($store_admin_id);
            if ($admin) {
                // 一个微信号只能绑定一个店员
                $old_admin = $wechat_user->storeAdmin;
                if ($old_admin) {
                    return trans('service.admin_error', [
                        'name'=>$wechat_user->decodeNickName(),
                        'old_account'=>$old_admin->account,
                        'new_account'=>$admin->account,
                    ]);
                }

                $admin->update(['wechat_user_id' => $wechat_user->id]);

                return trans('service.admin', ['name'=>$wechat_user->decodeNickName(), 'account'=>$admin->account]); // return "您好：{$wechat_user->decodeNickName()}，已成功绑定店员：{$admin->account}";
            } else {
                return trans('service.store_admin', ['id'=>$store_admin_id]); // return "对不起，没有找到对应店员[store_admin_id:{$store_admin_id}]";
            }
        } else {
            return trans('service.wechat_user', ['open_id'=>$open_id]); // return "对不起，没有找到用户[openid:{$open_id}]";
        }
    }

    /**
     * 处理临时素材 图片事件
     *
     * @return Image
     */
    private function handleReplyImage()
    {
        $this->scan_event->increment('scan_count');

        return new Image($this->scan_event->data['media_id']);
    }

    /**
     * 处理临时素材 视频事件
     *
     * @return Video
     */
    private function handleReplyVideo()
    {
        $this->scan_event->increment('scan_count');

        return new Video($this->scan_event->data['media_id']);
    }

    /**
     * @param $wechat_user
     *
     * @return bool
     */
    private function syncWechatUserLanguage(WechatUser $wechat_user): bool
    {
        $user_lang = $wechat_user->language;
        $device_lang = $this->scan_event->data['language'] ?? Language::default();

        /**
         * @uses \App\Http\Controllers\App\WechatUserController::loginQrCode()
         */
        if ($user_lang !== $device_lang) {
            return $wechat_user->update([
                'language' => $device_lang,
            ]);
        } else {
            return true;
        }
    }

    /**
     * @param array $message
     *
     * @return array|News|\Illuminate\Contracts\Translation\Translator|string|null
     */
    private function handleBoomGame(array $message)
    {
        $this->incrementScanCount();

        $wechat_user = $this->checkFindWechatUser($message['FromUserName']);

        return new News([
            new NewsItem([
                'title'       => '玩游戏获取奖品',
                'description' => '化妆品、折扣券等无限制获取',
                'url'         => $wechat_user->appWechatScanBoomGameUrl($this->scan_event),
                'image'       => self::NEWS_IMAGE_BOOM_GAME,
            ])
        ]);
    }

    /**
     * @param array $message
     *
     * @return array|News|\Illuminate\Contracts\Translation\Translator|string|null
     */
    private function handleCouponCard(array $message)
    {
        $wechat_user = $this->checkFindWechatUser($message['FromUserName']);

        if (! $this->scan_event->isScanned()) {
            $this->createCouponCard($wechat_user);
        }

        $this->incrementScanCount();

        return new News([
            new NewsItem([
                'title'       => '查看奖品',
                'description' => '游戏奖品详情',
                'url'         => $wechat_user->appWechatScanCouponCardUrl($this->scan_event->getKey()),
                'image'       => self::NEWS_IMAGE_COUPON_CARD,
            ])
        ]);
    }

    private function incrementScanCount(): int
    {
        return $this->scan_event->increment('scan_count');
    }

    private function checkFindWechatUser(string $openid): WechatUser
    {
        /** @var WechatUser $wechat_user */
        $wechat_user = WechatUser::findByOpenId($openid);
        if (! $wechat_user) {
            return trans('service.wechat_user', ['open_id'=>$openid]);
        }

        return $wechat_user;
    }

    /**
     * @param WechatUser $wechat_user
     */
    private function createCouponCard(WechatUser $wechat_user): void
    {
        $coupon_ticket_id = $this->scan_event->data['coupon_ticket_id'] ?? null;
        if ($coupon_ticket_id) {
            /** @var CouponTicket $coupon_ticket */
            $coupon_ticket = (new CouponTicket)->find($coupon_ticket_id);
            if ($coupon_ticket && $coupon_ticket->is_available) {
                (new CouponCard)->create([
                    'code'             => TimeHelper::microTimeSerial(),
                    'coupon_ticket_id' => $coupon_ticket->getKey(),
                    'wechat_user_id'   => $wechat_user->getKey(),
                    'started_at'       => now(),
                    'ended_at'         => now()->addDay($coupon_ticket->expired_in),
                    'is_used'          => CouponCard::NOT_USED,
                ]);
            }
        }
    }
}